﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.IO;

namespace MediaServer.Utilities
{
    /// <summary>
    /// Wrapper for a single directory object.
    /// </summary>
    public class SharedDirectory : IDIDLSerializable, IDIDLSearchable
    {
        protected string path;
        protected string parent;
        protected string name;
        protected string description;
        protected string id;

        protected List<SharedDirectory> dirs = new List<SharedDirectory>();
        protected List<SharedFile> files = new List<SharedFile>();

        /// <summary>
        /// Generates MD5 hash of this directory.
        /// </summary>
        private string GenerateID()
        {
            using (var md5 = MD5.Create())
            {
                md5.ComputeHash(Encoding.UTF8.GetBytes(this.path));
                return BitConverter.ToString(md5.Hash);
            }
        }

        /// <summary>
        /// Creates children of current directory based on path value.
        /// </summary>
        protected virtual void FindChildren()
        {
            if (Directory.Exists(this.path))
            {
                string[] dirs = Directory.GetDirectories(this.path);
                string[] files = Directory.GetFiles(this.path);

                Console.WriteLine(this.path);
                foreach (string dir in dirs)
                {
                    Console.WriteLine(dir);
                    this.dirs.Add(new SharedDirectory(dir, this.id));
                }

                foreach (string file in files)
                {
                    Console.WriteLine(file);
                    this.files.Add(new Utilities.SharedFile(file, this.id));
                }
            }
        }

        protected SharedDirectory()
        {
        }

        public SharedDirectory(string path, string parent)
        {
            Console.WriteLine(path);
            this.path = path;
            string[] parts = Regex.Split(path, @"(\\|/)");
            this.name = parts[parts.Length - 1];
            Console.WriteLine(name);
            this.description = "";
            this.parent = parent;
            this.id = this.GenerateID();
            this.FindChildren();
        }

        public List<IDIDLSerializable> GetChildren()
        {
            List<IDIDLSerializable> results = new List<IDIDLSerializable>();
            foreach (SharedDirectory dir in dirs)
            {
                results.Add((IDIDLSerializable)dir);
            }
            foreach (SharedFile file in files)
            {
                results.Add((IDIDLSerializable)file);
            }
            return results;
        }

        public string GetID()
        {
            return this.id;
        }

        public override string ToString() {
            return this.path;
        }

        /// <summary>
        /// Attempts to recursively find an object by given ID.
        /// </summary>
        /// <param name="id">ID of the object.</param>
        /// <returns>Returns first object matching the ID or null if not found.</returns>
        public IDIDLSerializable Find(string id)
        {
            if (this.id.Equals(id))
            {
                return this;
            }
            else
            {
                List<IDIDLSerializable> children = this.GetChildren();
                foreach (IDIDLSerializable child in children)
                {
                    IDIDLSerializable ret = child.Find(id);
                    if (ret != null)
                    {
                        return ret;
                    }
                }
                return null;
            }
        }

        /// <summary>
        /// Search a ContentDirectory service subtree for objects that match some search criteria.
        /// </summary>
        /// <returns>Returns list of matching objects within current subtree.</returns>
        public List<IDIDLSerializable> Search(string SearchCriteria)
        {
            List<IDIDLSerializable> results = new List<IDIDLSerializable>();

            foreach(SharedFile file in this.files)
            {
                if (file.Matches(SearchCriteria))
                {
                    results.Add(file);
                }
            }

            foreach (SharedDirectory dir in this.dirs)
            {
                if (dir.Matches(SearchCriteria))
                {
                    results.Add(dir);
                }
                results.AddRange(dir.Search(SearchCriteria));
            }

            return results;
        }

        /// <summary>
        /// Prepare raw results for Browse query.
        /// </summary>
        /// <returns>
        /// List containing direct children in current directory. 
        /// </returns>
        public List<IDIDLSerializable> Browse()
        {
            return this.GetChildren();
        }

        public XmlElement SingleXML(XmlDocument doc)
        {
            XmlElement container = doc.CreateElement("container");
            container.SetAttribute("id", this.id);
            container.SetAttribute("parentID", this.parent);
            container.SetAttribute("restricted", "1");
            container.SetAttribute("searchable", "1");
            container.AppendChild(doc.CreateElement("dc:title", "dc")).InnerText = this.name;
            container.AppendChild(doc.CreateElement("dc:description", "dc")).InnerText = this.description;
            container.AppendChild(doc.CreateElement("upnp:class", "upnp")).InnerText = "object.container.storageFolder";
            return container;
        }

        public bool Matches(string SearchCriteria)
        {
            Match match = Regex.Match(SearchCriteria, "^dc:title = \"(.*)\"$");
            return match.Groups.Count > 1 && this.name.Contains(match.Groups[1].Value);
        }
    }

    /// <summary>
    /// Class representing a top level virtual directory (Root, Music, Photos or Videos).
    /// </summary>
    public class VirtualDirectory : SharedDirectory
    {

        /// <summary>
        /// Create a virtual directory by providing its name, id and parent.
        /// </summary>
        /// <param name="name">Name of the virtual directory.</param>
        /// <param name="parent">ID of the parent object.</param>
        /// <param name="id">ID of the virtual directory.</param>
        public VirtualDirectory(string name, string parent, string id)
        {
            this.path = this.name = name;
            this.description = "";
            this.parent = parent;
            this.id = id;
        }

        protected override void FindChildren()
        {
            // Do nothing. Virtual directories have no children
            // based on actual file system, those have to be set
            // manually via the AddChildren() method.
        }

        public void AddChild(SharedDirectory child)
        {
            this.dirs.Add(child);
        }

        public void RemoveChild(SharedDirectory child)
        {
            this.dirs.Remove(child);
        }

        public override string ToString()
        {
            return this.name;
        }
    }
}
